#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
5sing音乐下载器 - 完整版
支持翻页、单曲下载、批量下载等功能
"""

import requests
import time
import hashlib
import json
import re
import random
import string
from typing import Dict, List, Optional
from bs4 import BeautifulSoup
import urllib.parse
import os
# from lxml import etree  # 使用BeautifulSoup代替

class FivesingCompleteDownloader:
    """5sing音乐下载器 - 完整版"""
    
    def __init__(self, use_proxy=False):
        """初始化下载器"""
        self.use_proxy = use_proxy
        self.proxies = []
        self.current_proxy = None
        self.proxy_stats = {}  # 代理统计信息
        self.proxy_history_file = "proxy_history.json"  # 代理历史文件
        self.blacklist = {}  # 代理黑名单（小黑屋）
        self.blacklist_file = "proxy_blacklist.json"  # 黑名单文件
        
        # 常量定义
        self.SEARCH_URL = "http://search.5sing.kugou.com/home/index?keyword="
        self.GET_URL = "https://5sservice.kugou.com/song/getsongurl"
        self.APP_ID = "2918"
        self.CLIENT_VER = "1000"
        self.VERSION = "6.6.72"
        self.SECRET_KEY = "5uytoxQewcvIc1gn1PlNF0T2jbbOzRl5"
        
        # 请求头
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36',
            'Accept': 'application/json, text/plain, */*',
            'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
            'Referer': 'http://5sing.kugou.com/'
        }
        
        # 初始化代理池
        if use_proxy:
            self._load_proxy_history()
            self._load_blacklist()
            self._load_proxies()
    
    def _load_proxy_history(self):
        """加载代理历史记录"""
        try:
            if os.path.exists(self.proxy_history_file):
                with open(self.proxy_history_file, 'r', encoding='utf-8') as f:
                    self.proxy_stats = json.load(f)
                print(f"📊 加载了代理历史记录: {len(self.proxy_stats)} 个代理统计")
            else:
                self.proxy_stats = {}
                print("📊 未找到代理历史记录，将创建新的记录")
        except Exception as e:
            print(f"⚠️ 加载代理历史失败: {e}")
            self.proxy_stats = {}
    
    def _load_blacklist(self):
        """加载代理黑名单"""
        try:
            if os.path.exists(self.blacklist_file):
                with open(self.blacklist_file, 'r', encoding='utf-8') as f:
                    self.blacklist = json.load(f)
                print(f"🚫 加载了黑名单: {len(self.blacklist)} 个代理")
            else:
                self.blacklist = {}
        except Exception as e:
            print(f"⚠️ 加载黑名单失败: {e}")
            self.blacklist = {}
    
    def _save_blacklist(self):
        """保存代理黑名单"""
        try:
            with open(self.blacklist_file, 'w', encoding='utf-8') as f:
                json.dump(self.blacklist, f, ensure_ascii=False, indent=2)
        except Exception as e:
            print(f"⚠️ 保存黑名单失败: {e}")
    
    def _save_proxy_history(self):
        """保存代理历史记录"""
        try:
            with open(self.proxy_history_file, 'w', encoding='utf-8') as f:
                json.dump(self.proxy_stats, f, ensure_ascii=False, indent=2)
        except Exception as e:
            print(f"⚠️ 保存代理历史失败: {e}")
    
    def _load_proxies(self):
        """加载代理池"""
        try:
            if os.path.exists('proxies.txt'):
                with open('proxies.txt', 'r', encoding='utf-8') as f:
                    self.proxies = [line.strip() for line in f if line.strip() and not line.startswith('#')]
                print(f"✅ 加载了 {len(self.proxies)} 个代理")
            else:
                print("⚠️ 未找到代理文件，使用直连模式")
                self.use_proxy = False
        except Exception as e:
            print(f"❌ 加载代理失败: {e}")
            self.use_proxy = False
    
    def _get_proxy(self):
        """获取随机代理"""
        if not self.use_proxy or not self.proxies:
            return None
        
        proxy = random.choice(self.proxies)
        return {
            'http': f'http://{proxy}',
            'https': f'http://{proxy}'
        }
    
    def test_proxy(self, proxy_url: str, timeout: int = 5) -> bool:
        """测试代理是否可用"""
        try:
            proxy_dict = {
                'http': f'http://{proxy_url}',
                'https': f'http://{proxy_url}'
            }
            
            # 测试代理连接 - 使用更简单的测试
            response = requests.get(
                'http://www.baidu.com', 
                proxies=proxy_dict, 
                timeout=timeout,
                headers=self.headers
            )
            
            if response.status_code == 200:
                # 记录代理成功使用
                self._record_proxy_usage(proxy_url, True)
                # 如果代理在黑名单中，移除它
                if proxy_url in self.blacklist:
                    self._remove_from_blacklist(proxy_url)
                return True
        except Exception as e:
            print(f"🔍 代理测试失败: {proxy_url} - {str(e)[:50]}")
        
        # 记录代理失败
        self._record_proxy_usage(proxy_url, False)
        
        # 检查是否需要加入黑名单
        stats = self.proxy_stats.get(proxy_url, {})
        failure_count = stats.get('failure_count', 0)
        
        if failure_count >= 2:  # 连续失败2次
            self._add_to_blacklist(proxy_url, "连续失败2次")
        
        return False
    
    def _record_proxy_usage(self, proxy_url: str, success: bool):
        """记录代理使用情况"""
        if proxy_url not in self.proxy_stats:
            self.proxy_stats[proxy_url] = {
                'total_attempts': 0,
                'success_count': 0,
                'failure_count': 0,
                'last_used': None,
                'success_rate': 0.0
            }
        
        stats = self.proxy_stats[proxy_url]
        stats['total_attempts'] += 1
        stats['last_used'] = time.strftime('%Y-%m-%d %H:%M:%S')
        
        if success:
            stats['success_count'] += 1
        else:
            stats['failure_count'] += 1
        
        # 计算成功率
        if stats['total_attempts'] > 0:
            stats['success_rate'] = stats['success_count'] / stats['total_attempts']
        
        # 保存历史记录
        self._save_proxy_history()
    
    def get_working_proxy(self, max_attempts: int = 5) -> Optional[Dict]:
        """获取可用的代理（优先使用成功率高的代理）"""
        if not self.use_proxy or not self.proxies:
            return None
        
        # 如果有当前代理且不在黑名单中，优先使用
        if self.current_proxy and not self._is_in_blacklist(self.current_proxy):
            if self.test_proxy(self.current_proxy):
                return {
                    'http': f'http://{self.current_proxy}',
                    'https': f'http://{self.current_proxy}'
                }
        
        # 按成功率排序代理
        sorted_proxies = self._get_sorted_proxies()
        
        attempts = 0
        for proxy_url in sorted_proxies:
            if attempts >= max_attempts:
                break
                
            if self.test_proxy(proxy_url):
                print(f"✅ 找到可用代理: {proxy_url}")
                self.current_proxy = proxy_url  # 记录当前代理
                return {
                    'http': f'http://{proxy_url}',
                    'https': f'http://{proxy_url}'
                }
            
            attempts += 1
            print(f"⚠️ 代理 {proxy_url} 不可用，尝试下一个...")
        
        print("❌ 所有代理都不可用，使用直连模式")
        self.current_proxy = None
        return None
    
    def _get_sorted_proxies(self):
        """获取按成功率排序的代理列表（排除黑名单）"""
        # 过滤掉黑名单中的代理
        available_proxies = []
        for proxy in self.proxies:
            if not self._is_in_blacklist(proxy):
                available_proxies.append(proxy)
            else:
                print(f"🚫 跳过黑名单代理: {proxy}")
        
        # 为没有记录的代理添加默认统计
        for proxy in available_proxies:
            if proxy not in self.proxy_stats:
                self.proxy_stats[proxy] = {
                    'total_attempts': 0,
                    'success_count': 0,
                    'failure_count': 0,
                    'last_used': None,
                    'success_rate': 0.5  # 默认成功率50%
                }
        
        # 按成功率降序排序
        sorted_proxies = sorted(
            available_proxies, 
            key=lambda x: self.proxy_stats.get(x, {}).get('success_rate', 0.0),
            reverse=True
        )
        
        return sorted_proxies
    
    def show_proxy_stats(self):
        """显示代理统计信息"""
        if not self.proxy_stats:
            print("📊 暂无代理统计信息")
            return
        
        print("\n📊 代理统计信息:")
        print("-" * 80)
        print(f"{'代理地址':<20} {'成功率':<10} {'成功次数':<10} {'失败次数':<10} {'最后使用':<20}")
        print("-" * 80)
        
        # 按成功率排序显示
        sorted_stats = sorted(
            self.proxy_stats.items(),
            key=lambda x: x[1].get('success_rate', 0.0),
            reverse=True
        )
        
        for proxy_url, stats in sorted_stats:
            success_rate = stats.get('success_rate', 0.0) * 100
            success_count = stats.get('success_count', 0)
            failure_count = stats.get('failure_count', 0)
            last_used = stats.get('last_used', '从未使用') or '从未使用'
            
            print(f"{proxy_url:<20} {success_rate:>6.1f}% {success_count:>8} {failure_count:>8} {last_used:<20}")
        
        print("-" * 80)
    
    def test_proxy_list(self, proxy_list: str):
        """测试代理列表（逗号分隔）"""
        proxies = [p.strip() for p in proxy_list.split(',') if p.strip()]
        print(f"🔍 开始测试 {len(proxies)} 个代理...")
        
        working_proxies = []
        failed_proxies = []
        
        for i, proxy in enumerate(proxies, 1):
            print(f"📡 测试 {i}/{len(proxies)}: {proxy}")
            
            if self.test_proxy(proxy):
                working_proxies.append(proxy)
                print(f"✅ {proxy} - 可用")
            else:
                failed_proxies.append(proxy)
                print(f"❌ {proxy} - 不可用")
        
        # 将可用的代理添加到proxies.txt
        if working_proxies:
            self._add_proxies_to_file(working_proxies)
            print(f"\n✅ 成功添加 {len(working_proxies)} 个可用代理到文件")
        
        print(f"\n📊 测试结果:")
        print(f"✅ 可用: {len(working_proxies)} 个")
        print(f"❌ 不可用: {len(failed_proxies)} 个")
        
        if working_proxies:
            print(f"\n可用代理列表:")
            for proxy in working_proxies:
                print(f"  - {proxy}")
    
    def _add_proxies_to_file(self, new_proxies: List[str]):
        """添加代理到文件"""
        try:
            # 读取现有代理
            existing_proxies = set()
            if os.path.exists('proxies.txt'):
                with open('proxies.txt', 'r', encoding='utf-8') as f:
                    existing_proxies = set(line.strip() for line in f if line.strip() and not line.startswith('#'))
            
            # 添加新代理
            with open('proxies.txt', 'a', encoding='utf-8') as f:
                for proxy in new_proxies:
                    if proxy not in existing_proxies:
                        f.write(f"{proxy}\n")
                        existing_proxies.add(proxy)
            
            # 重新加载代理池
            self._load_proxies()
            
        except Exception as e:
            print(f"❌ 添加代理到文件失败: {e}")
    
    def _add_to_blacklist(self, proxy_url: str, reason: str = "连续失败"):
        """添加代理到黑名单"""
        self.blacklist[proxy_url] = {
            'reason': reason,
            'added_time': time.strftime('%Y-%m-%d %H:%M:%S'),
            'retry_count': self.blacklist.get(proxy_url, {}).get('retry_count', 0) + 1
        }
        self._save_blacklist()
        print(f"🚫 代理 {proxy_url} 已加入黑名单 ({reason})")
    
    def _is_in_blacklist(self, proxy_url: str) -> bool:
        """检查代理是否在黑名单中"""
        if proxy_url not in self.blacklist:
            return False
        
        # 检查是否超过10分钟
        blacklist_time = self.blacklist[proxy_url].get('added_time')
        if blacklist_time:
            try:
                from datetime import datetime, timedelta
                added_time = datetime.strptime(blacklist_time, '%Y-%m-%d %H:%M:%S')
                if datetime.now() - added_time > timedelta(minutes=10):
                    # 超过10分钟，从黑名单移除
                    del self.blacklist[proxy_url]
                    self._save_blacklist()
                    return False
            except:
                pass
        
        return True
    
    def _remove_from_blacklist(self, proxy_url: str):
        """从黑名单移除代理"""
        if proxy_url in self.blacklist:
            del self.blacklist[proxy_url]
            self._save_blacklist()
            print(f"✅ 代理 {proxy_url} 已从黑名单移除")
    
    def generate_random_string(self, length: int = 27) -> str:
        """生成指定长度的随机字符串"""
        characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
        return ''.join(random.choice(characters) for _ in range(length))
    
    def md5_hash(self, input_string: str) -> str:
        """计算MD5哈希值"""
        return hashlib.md5(input_string.encode('utf-8')).hexdigest()
    
    def generate_signature(self, song_id: str, song_type: str) -> tuple:
        """生成请求签名"""
        # 生成参数
        mid = self.md5_hash(song_id)
        dfid = self.generate_random_string(27)
        client_time = str(int(time.time() * 1000))
        
        # 构建查询字符串（包含uuid参数）
        uuid = mid
        query_parts = [
            f"appid={self.APP_ID}",
            f"clienttime={client_time}",
            f"clientver={self.CLIENT_VER}",
            f"dfid={dfid}",
            f"mid={mid}",
            f"songid={song_id}",
            f"songtype={song_type}",
            f"uuid={uuid}",
            f"version={self.VERSION}"
        ]
        
        # 移除所有&符号
        query_string = ''.join(query_parts)
        
        # 添加SALT
        salted_query = self.SECRET_KEY + query_string + self.SECRET_KEY
        
        # 计算MD5签名
        signature = self.md5_hash(salted_query)
        
        return signature, mid, dfid, client_time
    
    def search_songs(self, keyword: str, song_type: str = "全部", page: int = 1) -> Dict:
        """搜索歌曲 - 使用正确的JSON API接口"""
        try:
            # 5sing的真正搜索API（从sarch.js中发现）
            search_api_url = "http://search.5sing.kugou.com/home/json"
            
            # 构建搜索参数（根据sarch.js中的参数）
            type_map = {"全部": 0, "原创": 1, "翻唱": 2, "伴奏": 3}
            type_param = type_map.get(song_type, 0)
            
            params = {
                'keyword': keyword,
                'page': page,
                'sort': 1,  # 默认排序
                'filter': 0,  # 默认筛选
                'type': type_param
            }
            
            print(f"🔍 搜索API: {search_api_url}")
            print(f"📋 搜索参数: {params}")
            
            # 发送请求到JSON API
            proxy = self.get_working_proxy() if self.use_proxy else None
            response = requests.get(search_api_url, params=params, headers=self.headers, proxies=proxy, timeout=10)
            response.raise_for_status()
            
            
            
            # 直接解析JSON响应
            data = response.json()
                      
            if data and 'list' in data:
                songs_data = data['list']
                page_info = data.get('pageInfo', {})
                total_count = page_info.get('totalCount', 0)
                
                print(f"✅ 获取到 {len(songs_data)} 首歌曲，总计 {total_count} 首")
                
                # 处理歌曲数据
                songs = []
                for item in songs_data:
                    # 处理歌曲名称，清理HTML标签
                    song_name = item.get('songName', '')
                    song_name = song_name.replace('<em class="keyword">', '').replace('</em>', '')
                    
                    # 处理歌手名称
                    musician = item.get('singer', '')
                    
                    # 获取原始歌曲类型
                    original_song_type = item.get('typeEname', 'yc')
                    
                    song_info = {
                        'song_id': str(item.get('songId', '')),
                        'song_name': song_name,
                        'song_type': self._get_song_type_name(original_song_type),
                        'original_song_type': original_song_type,  # 保存原始类型代码
                        'musician': musician,
                        'upload_time': item.get('createTime', ''),
                        'popularity': item.get('popularityCnt', 0),
                        'download_count': item.get('downloadCnt', 0),
                        'song_url': f"http://5sing.kugou.com/{original_song_type}/{item.get('songId', '')}.html"
                    }
                    songs.append(song_info)
                
                return {
                    'success': True,
                    'keyword': keyword,
                    'song_type': song_type,
                    'current_page': page,
                    'total_count': total_count,
                    'max_pages': page_info.get('totalPages', 1),
                    'songs': songs,
                    'has_next': page < page_info.get('totalPages', 1),
                    'has_prev': page > 1
                }
            else:
                return {
                    'success': False,
                    'error': 'API返回数据格式错误',
                    'songs': [],
                    'total_count': 0
                }
            
            if json_data:
                # 处理歌曲数据
                songs = []
                for item in json_data:
                    # 处理歌曲名称，清理HTML标签和Unicode编码
                    song_name = item.get('songName', '')
                    song_name = song_name.replace('<em class="keyword">', '').replace('</em>', '')
                    song_name = song_name.encode('utf-8').decode('unicode_escape')
                    
                    # 处理歌手名称
                    musician = item.get('singer', '')
                    musician = musician.encode('utf-8').decode('unicode_escape')
                    
                    # 获取原始歌曲类型
                    original_song_type = item.get('songType', 'yc')
                    
                    song_info = {
                        'song_id': str(item.get('songId', '')),
                        'song_name': song_name,
                        'song_type': self._get_song_type_name(original_song_type),
                        'original_song_type': original_song_type,  # 保存原始类型代码
                        'musician': musician,
                        'upload_time': item.get('createTime', ''),
                        'popularity': item.get('collectCnt', 0),
                        'download_count': item.get('downloadCnt', 0),
                        'song_url': f"http://5sing.kugou.com/{original_song_type}/{item.get('songId', '')}.html"
                    }
                    songs.append(song_info)
                
                return {
                    'success': True,
                    'keyword': keyword,
                    'song_type': song_type,
                    'current_page': page,
                    'total_count': total_count,
                    'max_pages': (total_count + 9) // 10,  # 每页10首
                    'songs': songs,
                    'has_next': page < (total_count + 9) // 10,
                    'has_prev': page > 1
                }
            else:
                return {
                    'success': False,
                    'error': '未找到歌曲数据',
                    'songs': [],
                    'total_count': 0
                }
                
        except Exception as e:
            return {
                'success': False,
                'error': f'搜索失败: {str(e)}',
                'songs': [],
                'total_count': 0
            }
    
    def _get_song_type_name(self, song_type: str) -> str:
        """获取歌曲类型中文名称"""
        type_map = {
            'yc': '原创',
            'fc': '翻唱', 
            'bz': '伴奏'
        }
        return type_map.get(song_type, '未知')
    
    def _get_song_type_code(self, song_type_name: str) -> str:
        """获取歌曲类型代码"""
        type_map = {
            '原创': 'yc',
            '翻唱': 'fc',
            '伴奏': 'bz',
            '未知': 'yc'  # 默认为原创
        }
        return type_map.get(song_type_name, 'yc')
    
    def get_download_url(self, song_id: str, song_type: str = "yc") -> Dict:
        """获取歌曲下载链接"""
        max_retries = 3
        
        for attempt in range(max_retries):
            try:
                print(f"🔍 获取下载链接: 歌曲ID={song_id}, 类型={song_type} (尝试 {attempt + 1}/{max_retries})")
                
                # 生成签名
                signature, mid, dfid, client_time = self.generate_signature(song_id, song_type)
                
                # 构建请求参数
                uuid = mid
                params = {
                    'appid': self.APP_ID,
                    'clientver': self.CLIENT_VER,
                    'mid': mid,
                    'uuid': uuid,
                    'dfid': dfid,
                    'songid': song_id,
                    'songtype': song_type,
                    'version': self.VERSION,
                    'clienttime': client_time,
                    'signature': signature
                }
                
                print(f"📡 发送请求到: {self.GET_URL}")
                print(f"📋 请求参数: {params}")
                
                # 发送请求
                proxy = self.get_working_proxy() if self.use_proxy else None
                response = requests.get(self.GET_URL, params=params, headers=self.headers, proxies=proxy, timeout=15)
                response.raise_for_status()
                
                print(f"📡 响应状态码: {response.status_code}")
                
                # 解析响应
                data = response.json()
                
                if data.get('success') and data.get('code') == 0:
                    song_data = data.get('data', {})
                    
                    return {
                        'success': True,
                        'song_id': song_data.get('songid'),
                        'song_name': song_data.get('songName', ''),
                        'song_type': song_data.get('songtype'),
                        'download_links': {
                            'lqurl': song_data.get('lqurl', ''),
                            'lqurl_backup': song_data.get('lqurl_backup', ''),
                            'hqurl': song_data.get('hqurl', ''),
                            'hqurl_backup': song_data.get('hqurl_backup', ''),
                            'squrl': song_data.get('squrl', ''),
                            'squrl_backup': song_data.get('squrl_backup', '')
                        },
                        'file_info': {
                            'lqsize': song_data.get('lqsize', 0),
                            'hqsize': song_data.get('hqsize', 0),
                            'sqsize': song_data.get('sqsize', 0)
                        }
                    }
                else:
                    print(f"⚠️ API返回错误: {data.get('message', '未知错误')}")
                    if attempt < max_retries - 1:
                        print(f"🔄 重试中...")
                        time.sleep(2)
                        continue
                    else:
                        return {
                            'success': False,
                            'error': f"API返回错误: {data.get('message', '未知错误')}",
                            'code': data.get('code')
                        }
                        
            except Exception as e:
                print(f"❌ 请求异常: {str(e)}")
                if attempt < max_retries - 1:
                    print(f"🔄 重试中...")
                    time.sleep(2)
                    continue
                else:
                    return {
                        'success': False,
                        'error': f"请求异常: {str(e)}"
                    }
        
        return {
            'success': False,
            'error': '所有重试都失败了'
        }
    
    def get_best_download_url(self, song_id: str, song_type: str = "yc") -> Optional[str]:
        """获取最佳下载链接"""
        result = self.get_download_url(song_id, song_type)
        
        if result['success']:
            links = result['download_links']
            
            # 按质量优先级选择链接
            if links['squrl']:
                return links['squrl']
            elif links['squrl_backup']:
                return links['squrl_backup']
            elif links['hqurl']:
                return links['hqurl']
            elif links['hqurl_backup']:
                return links['hqurl_backup']
            elif links['lqurl']:
                return links['lqurl']
            elif links['lqurl_backup']:
                return links['lqurl_backup']
        
        return None
    
    def download_song(self, song_info: Dict, download_dir: str = "downloaded_songs") -> bool:
        """下载单首歌曲"""
        try:
            song_id = song_info['song_id']
            song_name = song_info['song_name']
            song_type_name = song_info['song_type']
            
            # 优先使用原始歌曲类型，如果没有则转换
            if 'original_song_type' in song_info:
                song_type_code = song_info['original_song_type']
            else:
                song_type_code = self._get_song_type_code(song_type_name)
            
            print(f"🔍 尝试获取下载链接: {song_name} (类型: {song_type_name} -> {song_type_code})")
            
            # 获取下载链接
            download_url = self.get_best_download_url(song_id, song_type_code)
            
            if not download_url:
                print(f"❌ 无法获取下载链接: {song_name}")
                return False
            
            # 创建下载目录
            os.makedirs(download_dir, exist_ok=True)
            
            # 下载文件
            proxy = self.get_working_proxy() if self.use_proxy else None
            response = requests.get(download_url, proxies=proxy, timeout=30)
            response.raise_for_status()
            
            # 保存文件
            filename = f"{song_name}_{song_id}.mp3"
            # 清理文件名中的非法字符
            filename = re.sub(r'[<>:"/\\|?*]', '_', filename)
            filepath = os.path.join(download_dir, filename)
            
            with open(filepath, 'wb') as f:
                f.write(response.content)
            
            print(f"✅ 下载完成: {filename}")
            return True
            
        except Exception as e:
            print(f"❌ 下载失败: {song_name} - {str(e)}")
            return False
    
    def interactive_search(self):
        """交互式搜索和下载"""
        print("\n🔍 交互式搜索和下载")
        keyword = input("请输入搜索关键词: ").strip()
        if not keyword:
            print("❌ 搜索关键词不能为空")
            return
        
        print("请选择歌曲类型:")
        print("1. 全部")
        print("2. 原创") 
        print("3. 翻唱")
        print("4. 伴奏")
        type_choice = input("请输入数字 (1-4，默认1): ").strip() or "1"
        
        type_map = {"1": "全部", "2": "原创", "3": "翻唱", "4": "伴奏"}
        song_type = type_map.get(type_choice, "全部")
        
        print(f"\n🔍 交互式搜索: {keyword}")
        
        # 搜索第一页获取总数
        result = self.search_songs(keyword, song_type, 1)
        
        if not result['success']:
            print(f"❌ 搜索失败: {result.get('error', '未知错误')}")
            return
        
        total_count = result['total_count']
        max_pages = result['max_pages']
        
        print(f"📊 搜索到总计 {total_count:,} 首歌曲")
        print(f"📄 共约 {max_pages:,} 页")
        
        current_page = 1
        
        while True:
            print(f"\n=== 正在加载第 {current_page} 页 ===")
            
            # 获取当前页数据
            page_result = self.search_songs(keyword, song_type, current_page)
            
            if not page_result['success']:
                print(f"❌ 获取第 {current_page} 页失败: {page_result.get('error', '未知错误')}")
                break
            
            songs = page_result['songs']
            
            if not songs:
                print("❌ 未找到歌曲")
                break
            
            # 显示搜索结果
            print(f"\n📋 第 {current_page} 页搜索结果 (共{len(songs)}首):")
            print(f"📊 显示第 {(current_page-1)*10+1}-{current_page*10} 首 (总计 {total_count:,} 首)")
            print("-" * 80)
            
            for i, song in enumerate(songs, 1):
                type_icon = {"原创": "🎼", "翻唱": "🎤", "伴奏": "🎵"}.get(song['song_type'], "🎵")
                print(f"{i:2d}. {type_icon} {song['song_name']} - {song['musician']} ({song['song_type']}) [ID: {song['song_id']}]")
            
            print("-" * 80)
            
            # 操作选项
            print("\n📋 操作选项:")
            print("1-10. 下载对应歌曲")
            print("n. 下一页")
            print("p. 上一页")
            print("g. 跳转到指定页")
            print("a. 下载当前页所有歌曲")
            print("q. 退出搜索")
            
            choice = input("\n请选择操作: ").strip().lower()
            
            if choice == 'q':
                break
            elif choice == 'n':
                if current_page < max_pages:
                    current_page += 1
                else:
                    print("❌ 已经是最后一页")
            elif choice == 'p':
                if current_page > 1:
                    current_page -= 1
                else:
                    print("❌ 已经是第一页")
            elif choice == 'g':
                try:
                    page = int(input(f"请输入页码 (1-{max_pages}): "))
                    if 1 <= page <= max_pages:
                        current_page = page
                    else:
                        print("❌ 页码超出范围")
                except ValueError:
                    print("❌ 请输入有效数字")
            elif choice == 'a':
                print(f"\n📥 开始下载第 {current_page} 页所有歌曲...")
                success_count = 0
                for i, song in enumerate(songs, 1):
                    print(f"\n📥 处理 {i}/{len(songs)}: {song['song_name']} - {song['musician']}")
                    if self.download_song(song):
                        success_count += 1
                    time.sleep(1)  # 避免请求过快
                print(f"\n📊 批量下载完成: 成功 {success_count}/{len(songs)} 首")
            elif choice.isdigit():
                song_index = int(choice) - 1
                if 0 <= song_index < len(songs):
                    song = songs[song_index]
                    print(f"\n📥 下载歌曲: {song['song_name']} - {song['musician']}")
                    if self.download_song(song):
                        print("✅ 下载成功")
                    else:
                        print("❌ 下载失败")
                else:
                    print("❌ 无效的歌曲编号")
            else:
                print("❌ 无效的操作")
    
    def fetch_proxies_from_sources(self):
        """从免费代理网站采集代理"""
        print("🔍 开始从免费代理网站采集代理...")
        
        all_proxies = []
        
        # 代理采集源列表
        sources = [
            ("66代理", self._fetch_66ip),
            ("开心代理", self._fetch_kxdaili),
            ("快代理", self._fetch_kuaidaili),
            ("云代理", self._fetch_ip3366),
            ("小幻代理", self._fetch_ihuan),
            ("免费代理库", self._fetch_jiangxianli),
            ("89代理", self._fetch_89ip)
        ]
        
        for name, fetch_func in sources:
            try:
                print(f"📡 正在采集 {name}...")
                proxies = list(fetch_func())
                all_proxies.extend(proxies)
                print(f"✅ {name}: 采集到 {len(proxies)} 个代理")
                time.sleep(1)  # 避免请求过快
            except Exception as e:
                print(f"❌ {name}: 采集失败 - {str(e)[:50]}")
        
        print(f"\n📊 总共采集到 {len(all_proxies)} 个代理")
        return all_proxies
    
    def _fetch_66ip(self):
        """66代理"""
        try:
            url = "http://www.66ip.cn/"
            response = requests.get(url, timeout=10)
            if response.status_code == 200:
                soup = BeautifulSoup(response.text, 'html.parser')
                tables = soup.find_all('table')
                if len(tables) >= 3:
                    table = tables[2]  # 第三个表格
                    rows = table.find_all('tr')[1:]  # 跳过表头
                    for row in rows:
                        cells = row.find_all('td')
                        if len(cells) >= 2:
                            ip = cells[0].get_text().strip()
                            port = cells[1].get_text().strip()
                            if ip and port:
                                yield f"{ip}:{port}"
        except:
            pass
    
    def _fetch_kxdaili(self):
        """开心代理"""
        try:
            urls = ["http://www.kxdaili.com/dailiip.html", "http://www.kxdaili.com/dailiip/2/1.html"]
            for url in urls:
                response = requests.get(url, timeout=10)
                if response.status_code == 200:
                    soup = BeautifulSoup(response.text, 'html.parser')
                    table = soup.find('table', class_='active')
                    if table:
                        rows = table.find_all('tr')[1:]  # 跳过表头
                        for row in rows:
                            cells = row.find_all('td')
                            if len(cells) >= 2:
                                ip = cells[0].get_text().strip()
                                port = cells[1].get_text().strip()
                                if ip and port:
                                    yield f"{ip}:{port}"
        except:
            pass
    
    def _fetch_kuaidaili(self):
        """快代理"""
        try:
            urls = [
                'https://www.kuaidaili.com/free/inha/1/',
                'https://www.kuaidaili.com/free/intr/1/'
            ]
            for url in urls:
                response = requests.get(url, timeout=10)
                if response.status_code == 200:
                    soup = BeautifulSoup(response.text, 'html.parser')
                    table = soup.find('table')
                    if table:
                        rows = table.find_all('tr')[1:]  # 跳过表头
                        for row in rows:
                            cells = row.find_all('td')
                            if len(cells) >= 2:
                                ip = cells[0].get_text().strip()
                                port = cells[1].get_text().strip()
                                if ip and port:
                                    yield f"{ip}:{port}"
        except:
            pass
    
    def _fetch_ip3366(self):
        """云代理"""
        try:
            url = "http://www.ip3366.net/"
            response = requests.get(url, timeout=10)
            if response.status_code == 200:
                soup = BeautifulSoup(response.text, 'html.parser')
                table = soup.find('table')
                if table:
                    rows = table.find_all('tr')[1:]  # 跳过表头
                    for row in rows:
                        cells = row.find_all('td')
                        if len(cells) >= 2:
                            ip = cells[0].get_text().strip()
                            port = cells[1].get_text().strip()
                            if ip and port:
                                yield f"{ip}:{port}"
        except:
            pass
    
    def _fetch_ihuan(self):
        """小幻代理"""
        try:
            url = "https://ip.ihuan.me/"
            response = requests.get(url, timeout=10)
            if response.status_code == 200:
                soup = BeautifulSoup(response.text, 'html.parser')
                table = soup.find('table')
                if table:
                    rows = table.find_all('tr')[1:]  # 跳过表头
                    for row in rows:
                        cells = row.find_all('td')
                        if len(cells) >= 2:
                            ip = cells[0].get_text().strip()
                            port = cells[1].get_text().strip()
                            if ip and port:
                                yield f"{ip}:{port}"
        except:
            pass
    
    def _fetch_jiangxianli(self):
        """免费代理库"""
        try:
            url = "http://ip.jiangxianli.com/"
            response = requests.get(url, timeout=10)
            if response.status_code == 200:
                soup = BeautifulSoup(response.text, 'html.parser')
                table = soup.find('table')
                if table:
                    rows = table.find_all('tr')[1:]  # 跳过表头
                    for row in rows:
                        cells = row.find_all('td')
                        if len(cells) >= 2:
                            ip = cells[0].get_text().strip()
                            port = cells[1].get_text().strip()
                            if ip and port:
                                yield f"{ip}:{port}"
        except:
            pass
    
    def _fetch_89ip(self):
        """89代理"""
        try:
            url = "https://www.89ip.cn/"
            response = requests.get(url, timeout=10)
            if response.status_code == 200:
                soup = BeautifulSoup(response.text, 'html.parser')
                table = soup.find('table')
                if table:
                    rows = table.find_all('tr')[1:]  # 跳过表头
                    for row in rows:
                        cells = row.find_all('td')
                        if len(cells) >= 2:
                            ip = cells[0].get_text().strip()
                            port = cells[1].get_text().strip()
                            if ip and port:
                                yield f"{ip}:{port}"
        except:
            pass
    
    def test_and_save_proxies(self, proxy_list: List[str]):
        """测试代理并保存有效的"""
        print(f"\n🔍 开始测试 {len(proxy_list)} 个代理...")
        
        working_proxies = []
        failed_proxies = []
        
        for i, proxy in enumerate(proxy_list, 1):
            print(f"📡 测试 {i}/{len(proxy_list)}: {proxy}")
            
            if self.test_proxy(proxy):
                working_proxies.append(proxy)
                print(f"✅ {proxy} - 可用")
            else:
                failed_proxies.append(proxy)
                print(f"❌ {proxy} - 不可用")
            
            time.sleep(0.5)  # 避免测试过快
        
        # 保存有效的代理到文件
        if working_proxies:
            self._add_proxies_to_file(working_proxies)
            print(f"\n✅ 成功保存 {len(working_proxies)} 个可用代理到文件")
        
        print(f"\n📊 测试结果:")
        print(f"✅ 可用: {len(working_proxies)} 个")
        print(f"❌ 不可用: {len(failed_proxies)} 个")
        
        return working_proxies, failed_proxies
    

def main():
    """主函数"""
    print("=" * 60)
    print("  ██╗      ███╗       ██╗    █████████╗")
    print("  ╚██╗    ██ ██╗    ██╔╝       ╚══███╔╝")
    print("   ╚██╗  ██╔╝ ██╗  ██╔╝         ███╔╝")
    print("    ╚██ ██╔╝   ██ ██╔╝        ███╔╝")
    print("     ╚██╔╝      ██╔╝        █████████╗")
    print("      ╚═╝       ╚═╝           ╚══════╝")
    print("=" * 60)

    print("⚠️  注意：为防止IP被封，建议启用代理池")
    print("=" * 60)
    
    # 询问是否启用代理
    use_proxy = input("是否启用代理池? (y/n, 默认y): ").strip().lower()
    if use_proxy == 'n':
        print("⚠️ 使用直连模式")
        use_proxy = False
    else:
        print("✅ 启用代理池模式")
        use_proxy = True
    
    # 创建下载器实例
    downloader = FivesingCompleteDownloader(use_proxy=use_proxy)
    
    while True:
        print("\n" + "=" * 50)
        print("🎵 音乐下载器 - 伪造认证版")
        print("=" * 50)
        print("选择功能:")
        print("1. 交互式搜索和下载")
        print("2. 查看代理状态")
        print("3. 测试代理连接")
        print("4. 重新加载代理池")
        print("5. 显示代理统计")
        print("6. 测试代理列表")
        print("7. 从免费网站采集代理")
        print("8. 退出")
        print("=" * 50)
        
        choice = input("请选择 (1-8): ").strip()
        
        if choice == '1':
            downloader.interactive_search()
        elif choice == '2':
            if downloader.use_proxy:
                print(f"📊 代理池状态: 已加载 {len(downloader.proxies)} 个代理")
                print(f"📈 历史记录: {len(downloader.proxy_stats)} 个代理统计")
                if downloader.current_proxy:
                    print(f"🔄 当前代理: {downloader.current_proxy}")
            else:
                print("📊 代理池状态: 未启用")
        elif choice == '3':
            if downloader.use_proxy:
                print("🔍 测试代理连接...")
                working_proxy = downloader.get_working_proxy()
                if working_proxy:
                    print("✅ 找到可用代理")
                else:
                    print("❌ 没有可用的代理")
            else:
                print("❌ 代理功能未启用")
        elif choice == '4':
            if downloader.use_proxy:
                print("🔄 重新加载代理池...")
                downloader._load_proxies()
            else:
                print("❌ 代理功能未启用")
        elif choice == '5':
            if downloader.use_proxy:
                downloader.show_proxy_stats()
            else:
                print("❌ 代理功能未启用")
        elif choice == '6':
            proxy_list = input("请输入代理列表（逗号分隔）: ").strip()
            if proxy_list:
                downloader.test_proxy_list(proxy_list)
            else:
                print("❌ 请输入有效的代理列表")
        elif choice == '7':
            print("🔍 从免费网站采集代理...")
            # 采集代理
            all_proxies = downloader.fetch_proxies_from_sources()
            if all_proxies:
                # 测试并保存有效的代理
                working_proxies, failed_proxies = downloader.test_and_save_proxies(all_proxies)
                print(f"\n🎉 采集完成！获得 {len(working_proxies)} 个可用代理")
            else:
                print("❌ 未采集到任何代理")
        elif choice == '8':
            print("👋 再见！")
            break
        else:
            print("❌ 无效选择")


def main():
    """主函数"""
    print("=" * 60)
    print("  ██╗      ███╗       ██╗    █████████╗")
    print("  ╚██╗    ██ ██╗    ██╔╝       ╚══███╔╝")
    print("   ╚██╗  ██╔╝ ██╗  ██╔╝         ███╔╝")
    print("    ╚██ ██╔╝   ██ ██╔╝        ███╔╝")
    print("     ╚██╔╝      ██╔╝        █████████╗")
    print("      ╚═╝       ╚═╝           ╚══════╝")
    print("=" * 60)
    print("⚠️  注意：为防止IP被封，建议启用代理池")
    print("=" * 60)
    
    use_proxy = input("是否启用代理池? (y/n, 默认y): ").strip().lower()
    use_proxy = use_proxy != 'n'
    
    downloader = FivesingCompleteDownloader(use_proxy=use_proxy)
    
    while True:
        print("\n" + "=" * 50)
        print("🎵 音乐下载器 - 伪造认证版")
        print("=" * 50)
        print("选择功能:")
        print("1. 交互式搜索和下载")
        print("2. 查看代理状态")
        print("3. 测试代理连接")
        print("4. 重新加载代理池")
        print("5. 显示代理统计")
        print("6. 测试代理列表")
        print("7. 从免费网站采集代理")
        print("8. 退出")
        print("=" * 50)
        
        choice = input("请选择 (1-8): ").strip()
        
        if choice == '1':
            downloader.interactive_search()
        elif choice == '2':
            if downloader.use_proxy:
                print(f"📊 代理池状态: 已加载 {len(downloader.proxies)} 个代理")
                print(f"📈 历史记录: {len(downloader.proxy_stats)} 个代理统计")
                if downloader.current_proxy:
                    print(f"🔄 当前代理: {downloader.current_proxy}")
            else:
                print("📊 代理池状态: 未启用")
        elif choice == '3':
            if downloader.use_proxy:
                print("🔍 测试代理连接...")
                working_proxy = downloader.get_working_proxy()
                if working_proxy:
                    print("✅ 找到可用代理")
                else:
                    print("❌ 没有可用的代理")
            else:
                print("❌ 代理功能未启用")
        elif choice == '4':
            if downloader.use_proxy:
                print("🔄 重新加载代理池...")
                downloader._load_proxies()
            else:
                print("❌ 代理功能未启用")
        elif choice == '5':
            if downloader.use_proxy:
                downloader.show_proxy_stats()
            else:
                print("❌ 代理功能未启用")
        elif choice == '6':
            proxy_list = input("请输入代理列表（逗号分隔）: ").strip()
            if proxy_list:
                downloader.test_proxy_list(proxy_list)
            else:
                print("❌ 请输入有效的代理列表")
        elif choice == '7':
            print("🔍 从免费网站采集代理...")
            # 采集代理
            all_proxies = downloader.fetch_proxies_from_sources()
            if all_proxies:
                # 测试并保存有效的代理
                working_proxies, failed_proxies = downloader.test_and_save_proxies(all_proxies)
                print(f"\n🎉 采集完成！获得 {len(working_proxies)} 个可用代理")
            else:
                print("❌ 未采集到任何代理")
        elif choice == '8':
            print("👋 再见！")
            break
        else:
            print("❌ 无效选择")

if __name__ == "__main__":
    main()
